#include "ScnsDs18b20.c.h"

#if defined(SCNS_DS18B20_ENABLE)&&SCNS_DS18B20_ENABLE==1

//@scnsBsp:https://gitee.com/huoxingdawang/scnsDemos-STM32F767IGT6_AlientekApollo/tree/master/Libraries/scnsBsp/STM32F767IGT6_AlientekApollo/


#include "ScnsTimer.h"
#include "ScnsSmctp.h"
#include "ScnsCrc.h"

static struct
{
    uint8 inited;
    uint64 us;
}rd[SCNS_DS18B20_MAX];

ScnsDs18b20Result scnsDs18b20Result[SCNS_DS18B20_MAX];

static void reset(ScnsDs18b20Enum ds1N)
{
    scnsBspDs18b20SetDir(ds1N,0);
    scnsBspDs18b20Set(ds1N,0);
    scnsDelayUs(750);
    scnsBspDs18b20Set(ds1N,1);
    scnsDelayUs(50);
}

static uint8 check(ScnsDs18b20Enum ds1N)
{
    scnsBspDs18b20SetDir(ds1N,1);
    for(uint16 retry=0;scnsBspDs18b20Get(ds1N);retry+=1)
    {
        if(retry>=200)return 1;
        scnsDelayUs(1);
    }
    for(uint16 retry=0;!scnsBspDs18b20Get(ds1N);retry+=1)
    {
        if(retry>=200)return 1;
        scnsDelayUs(1);
    }
    return 0;
}

static uint8 Rb(ScnsDs18b20Enum ds1N)
{
    scnsBspDs18b20SetDir(ds1N,0);
    scnsBspDs18b20Set(ds1N,0);
    scnsDelayUs(2);
    scnsBspDs18b20Set(ds1N,1);
    scnsBspDs18b20SetDir(ds1N,1);
    scnsDelayUs(12);
    uint8 data=scnsBspDs18b20Get(ds1N)?1:0;
    scnsDelayUs(50);
    return data;
}

static uint8 RB(ScnsDs18b20Enum ds1N)
{
    uint8 dat=0;
    for(uint8 i=0;i<8;++i)
        dat|=Rb(ds1N)<<i;
    return dat;
}

static void Wb(ScnsDs18b20Enum ds1N,uint8 dat)
{
    scnsBspDs18b20SetDir(ds1N,0);
    if(dat)
    {
        scnsBspDs18b20Set(ds1N,0);
        scnsDelayUs(2);
        scnsBspDs18b20Set(ds1N,1);
        scnsDelayUs(60);
    }
    else
    {
        scnsBspDs18b20Set(ds1N,0);
        scnsDelayUs(60);
        scnsBspDs18b20Set(ds1N,1);
        scnsDelayUs(2);
    }
}

static void WB(ScnsDs18b20Enum ds1N,uint8 dat)
{
    for(uint8 j=0;j<8;++j)
    {
        Wb(ds1N,dat&0x01);
        dat=dat>>1;
    }
}

static void sendId(ScnsDs18b20Enum ds1N)
{
#if defined(SCNS_BSP_DS18B20_IGNORE_ID)&&SCNS_BSP_DS18B20_IGNORE_ID==1
    WB(ds1N,0xcc);
#else
    WB(ds1N,0x55);
    for(uint8 i=0;i<8;++i)
        WB(ds1N,scnsBspBs18b20Id[ds1N][i]);
#endif
}

static ScnsDs18b20Status RD(ScnsDs18b20Enum ds1N,uint8 data[9])
{
    reset(ds1N);
    check(ds1N);
    sendId(ds1N);
    WB(ds1N,0xBE);
    
    for(uint8 i=0;i<9;++i)
        data[i]=RB(ds1N);
    
    if(scnsCrc8OneWire(0,data,0,8,8)!=data[8])
        return SCNS_DS18B20_STATUS_CRC_FAILED;
    return SCNS_DS18B20_STATUS_OK;
}

static void start(ScnsDs18b20Enum ds1N)
{
    reset(ds1N);
    check(ds1N);
    sendId(ds1N);
    WB(ds1N,0x44);
}

static uint8 rc(ScnsDs18b20Enum ds1N)
{
    reset(ds1N);
    return check(ds1N);
}

ScnsDs18b20Status scnsDs18b20Init(ScnsDs18b20Enum ds1N)
{
    SCNS_ASSERT_ON_RUN(ds1N<SCNS_DS18B20_MAX);
    scnsCheck(ScnsDs18b20Status,scnsBspDs18b20Init(ds1N),SCNS_DS18B20_STATUS_OK);
    scnsCheckTimeOut(200*1000,rc(ds1N)==0,SCNS_DS18B20_STATUS_TIME_OUT);
    uint8 data[9];
    scnsCheckTimeOut(200*1000,RD(ds1N,data)==SCNS_DS18B20_STATUS_OK,SCNS_DS18B20_STATUS_CRC_FAILED);
    rd[ds1N].inited=1;
    return SCNS_DS18B20_STATUS_OK;
}

void scnsDs18b20Interrupt(ScnsDs18b20Enum ds1N)
{
    SCNS_ASSERT_ON_RUN(ds1N<SCNS_DS18B20_MAX);
    if(!rd[ds1N].inited)
        return;
    const uint64 t=scnsTimerGetUs();
    if(100*1000+rd[ds1N].us>=t)
        return;
    rd[ds1N].us=t;
    
    uint8 data[9];
    if(SCNS_DS18B20_STATUS_OK!=RD(ds1N,data))return;
    float temp=0.0625f;
    if(data[1]>7)
    {
        data[1]=~data[1];
        data[0]=~data[0];
        temp=-0.0625f;
    }
    scnsDs18b20Result[ds1N].temperature=((((uint16)data[1])<<8)|data[0])*temp;
    
    start(ds1N);
    scnsBspDs18b20ResultUpdate(ds1N);
}

void scnsDs18b20PrintId(ScnsDs18b20Enum ds1N)
{
    SCNS_ASSERT_ON_RUN(ds1N<SCNS_DS18B20_MAX);
    reset(ds1N);
    check(ds1N);
    WB(ds1N,0x33);
    uint8 data[8];
    for(uint8 m=0;m<8;++m)
        data[m]=RB(ds1N);
#if defined(SCNS_SMCTP_ENABLE)&&SCNS_SMCTP_ENABLE==1
    while(scnsSmctpCanSend(SCNS_SMCTP_CHANNEL_DEBUG,64)==SCNS_SMCTP_STATUS_FULL);
    printfFL("",);
    for(uint8 j=0;j<8;++j)
        printf("0X%02X,",(int)data[j]);
    printf("\r\n");
#endif
}

static uint8 search(ScnsDs18b20Enum ds1N,uint8*buff,uint8*stack,uint8 sp)
{
    uint8 len=64,pos=0;
    reset(ds1N);
    check(ds1N);
    WB(ds1N,0xF0);
    while(len--)
    {
        uint8 pb=Rb(ds1N);
        uint8 cb=Rb(ds1N);
        if(pb&&cb)
        {
            return 0;
        }
        else if(pb)
        {
            *(buff+pos/8)|=0x01<<(pos%8);
            Wb(ds1N,1);
            *(stack+pos/8)|=0x01<<(pos%8);
        }
        else if(cb)
        {
            *(buff+pos/8)&=~(0x01<<(pos%8));
            Wb(ds1N,0);
            *(stack+pos/8)|=0x01<<(pos%8);
        }
        else
        {
            if(sp==0xFF||pos>sp)
            {
                *(buff+pos/8)&=~(0x01<<(pos%8));
                Wb(ds1N,0);
                *(stack+pos/8)&=~(0x01<<(pos%8));
                sp=pos;
            }
            else if(pos==sp)
            {
                *(buff+pos/8)|=0x01<<(pos%8);
                Wb(ds1N,1);
                *(stack+pos/8)|=0x01<<(pos%8);
            }
            else
            {
                Wb(ds1N,*(buff+pos/8)>>(pos%8)&0x01);
            }
        }
        pos++;
    }
    while(sp>0&&*(stack+sp/8)>>(sp%8)&0x01==0x01)
        sp--;
    return sp;
}

void scnsDs18b20Search(void)
{
#if !(defined(SCNS_BSP_DS18B20_IGNORE_ID)&&SCNS_BSP_DS18B20_IGNORE_ID==1)
    for(int ds1N=0;ds1N<SCNS_DS18B20_MAX;++ds1N)
    {
        reset(ds1N);
        if(check(ds1N))
            continue;
        uint8 sp=0XFF;
        uint8 addr[8];
        uint8 stack[8];
        for(uint8 i=0;i<8;++i)
            addr[i]=stack[i]=0;
        while(sp)
        {
            sp=search(ds1N,addr,stack,sp);
#if defined(SCNS_SMCTP_ENABLE)&&SCNS_SMCTP_ENABLE==1
            while(scnsSmctpCanSend(SCNS_SMCTP_CHANNEL_DEBUG,64)==SCNS_SMCTP_STATUS_FULL);
            printfFL("%d:",ds1N);
            for(uint8 j=0;j<8;++j)
                printf("0X%02X,",(int)addr[j]);
            printf("\r\n");
#endif
        }
    }
#endif
}

#endif
